package org.yoome.jtools.distributelock;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;

import java.util.Collections;
import java.util.Objects;
import java.util.concurrent.TimeUnit;

public class DistributedLock {

    private static final String LOCK_SCRIPT = "if redis.call('set', KEYS[1], ARGV[1], 'NX', 'PX', ARGV[2]) then return 1 else return 0 end";

    private static final String UNLOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";

    private static final long EXPIRE_TIME = 60000L;

    private static final long SUCCESS = 1L;

    private static final long FAIL = 0L;

    @Autowired
    @Qualifier("distributedRedisTemplate")
    private RedisTemplate redisTemplate;

    public boolean lock(String key, String uid) {
        try {
            long endTime = System.currentTimeMillis() + EXPIRE_TIME;
            do {
                RedisScript<Long> script = new DefaultRedisScript<>(LOCK_SCRIPT, Long.class);
                Object execute = redisTemplate.execute(script, Collections.singletonList(key), uid, EXPIRE_TIME);
                if (Objects.equals(execute, SUCCESS)) {
                    return true;
                }
                TimeUnit.MILLISECONDS.sleep(100);
            } while (System.currentTimeMillis() < endTime);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return false;
    }


    public boolean unlock(String key, String uid) {
        RedisScript<Long> script = new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class);
        Object execute = redisTemplate.execute(script, Collections.singletonList(key), uid);
        if (Objects.equals(execute, SUCCESS)) {
            return true;
        }

        return false;
    }
}
